/**
 *  Copyright (C) 2021 Huawei Device Co., Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
#include <jni.h>
#include <string>
#include <iostream>
#include "hwcpipe.h"
#include "cpu_profiler.h"
#include "gpu_profiler.h"
#include "hwcpipe_log.h"

std::string
getGpuCounterInfo(hwcpipe::Measurements measurements, hwcpipe::GpuCounter counterValue) {
    double value;
    if (measurements.gpu) {
        const auto & counter = measurements.gpu->find(counterValue);
        if (counter != measurements.gpu->end()) {
            // Get the data stored in the counter, casted to the type you need
            value = counter->second.get<double> ();
        }
    }

    return std::to_string(value);
}

std::string
getCpuCounterInfo(hwcpipe::Measurements measurements, hwcpipe::CpuCounter counterValue) {
    long long value;
    if (measurements.cpu) {
        const auto & counter = measurements.cpu->find(counterValue);
        if (counter != measurements.cpu->end()) {
            // Get the data stored in the counter, casted to the type you need
            value = counter->second.get<long long> ();
        }
    }

    return std::to_string(value);
}


extern "C"
JNIEXPORT jstring JNICALL
Java_at_favre_lib_hwcpipedemo_slice_MainAbilitySlice_stringFromJNI(JNIEnv* env, jobject  obj) {
    std::string hello = "Hello from JNI C++ codes";
    int len = hello.size();
    jchar res[len];
    for (int i = 0; i < len; i++) {
        res[i] = (jchar)hello[i];
    }
    return env->NewString(res, len);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_at_favre_lib_hwcpipedemo_slice_MainAbilitySlice_getGpuInfoFromJni(JNIEnv* env, jobject  obj) {
    HWCPIPE_LOG("native：getGpuInfoFromJni");
    std::string value;
    hwcpipe::HWCPipe h;
    h.run();
    auto measurements=h.sample();
    value.append("GpuCycles:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::GpuCycles));
    value.append("\n");
    value.append("VertexComputeCycles:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::VertexComputeCycles));
    value.append("\n");
    value.append("FragmentCycles:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::FragmentCycles));
    value.append("\n");
    value.append("TilerCycles:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::TilerCycles));

    value.append("\n");
    value.append("CacheReadLookups:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::CacheReadLookups));
    value.append("\n");
    value.append("CacheWriteLookups:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::CacheWriteLookups));
    value.append("\n");
    value.append("ExternalMemoryReadAccesses:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::ExternalMemoryReadAccesses));
    value.append("\n");
    value.append("ExternalMemoryWriteAccesses:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::ExternalMemoryWriteAccesses));
    value.append("\n");
    value.append("ExternalMemoryReadStalls:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::ExternalMemoryReadStalls));
    value.append("\n");
    value.append("ExternalMemoryWriteStalls:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::ExternalMemoryWriteStalls));
    value.append("\n");
    value.append("ExternalMemoryReadBytes:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::ExternalMemoryReadBytes));
    value.append("\n");
    value.append("ExternalMemoryWriteBytes:");
    value.append(getGpuCounterInfo(measurements, hwcpipe::GpuCounter::ExternalMemoryWriteBytes));

    std::cout << value << std::endl;
    h.stop();
    int len = value.size();
    jchar res[len];
    for (int i = 0; i < len; i++) {
        res[i] = (jchar)value[i];
    }
    return env->NewString(res, len);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_at_favre_lib_hwcpipedemo_slice_MainAbilitySlice_getCpuInfoFromJni(JNIEnv* env, jobject  obj) {
    HWCPIPE_LOG("native：getCpuInfoFromJni");
    std::string value;
    hwcpipe::HWCPipe h;
    h.run();
    auto measurements=h.sample();
    value.append("Cycles:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Cycles));
    value.append("\n");
    value.append("Instructions:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Instructions));
    value.append("\n");
    value.append("CacheReferences:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::CacheReferences));
    value.append("\n");
    value.append("CacheMisses:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::CacheMisses));
    value.append("\n");
    value.append("BranchMisses:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::BranchMisses));
    value.append("\n");
    value.append("L1Accesses:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::L1Accesses));
    std::cout << value << std::endl;
    h.stop();
    int len = value.size();
    jchar res[len];
    for (int i = 0; i < len; i++) {
        res[i] = (jchar)value[i];
    }
    return env->NewString(res, len);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_at_favre_lib_hwcpipedemo_slice_MainAbilitySlice_createHWCPipeFromJson(JNIEnv* env, jobject  obj) {
    HWCPIPE_LOG("native：createHWCPipeFromJson");
    std::string json="{\"cpu\": [\"Cycles\", \"Instructions\"],\"gpu\": [\"GpuCycles\"]}";
    auto h = hwcpipe::HWCPipe((char*)json.c_str());
    std::string value;
    h.run();
    auto measurements=h.sample();
    value.append("cpu Cycles:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Cycles));
    value.append("\n");
    value.append("cpu Instructions:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Instructions));
    value.append("\n");
    value.append("gpu GpuCycles:");
    value.append(getGpuCounterInfo(measurements,hwcpipe::GpuCounter::GpuCycles));
    value.append("\n");
    std::cout << value << std::endl;
    h.stop();
    int len = value.size();
    jchar res[len];
    for (int i = 0; i < len; i++) {
        res[i] = (jchar)value[i];
    }
    return env->NewString(res, len);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_at_favre_lib_hwcpipedemo_slice_MainAbilitySlice_createHWCPipePart(JNIEnv* env, jobject  obj) {
    HWCPIPE_LOG("native：createHWCPipePart");
    auto h = hwcpipe::HWCPipe({hwcpipe::CpuCounter::Cycles, hwcpipe::CpuCounter::Instructions}, {hwcpipe::GpuCounter::GpuCycles});
    std::string value;
    h.run();
    auto measurements=h.sample();
    value.append("cpu Cycles:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Cycles));
    value.append("\n");
    value.append("cpu Instructions:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Instructions));
    value.append("\n");
    value.append("gpu GpuCycles:");
    value.append(getGpuCounterInfo(measurements,hwcpipe::GpuCounter::GpuCycles));
    value.append("\n");
    std::cout << value << std::endl;
    h.stop();
    int len = value.size();
    jchar res[len];
    for (int i = 0; i < len; i++) {
        res[i] = (jchar)value[i];
    }
    return env->NewString(res, len);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_at_favre_lib_hwcpipedemo_slice_MainAbilitySlice_enableCpuCounter(JNIEnv* env, jobject  obj) {
    HWCPIPE_LOG("native：enableCpuCounter");
    hwcpipe::HWCPipe h;
    std::string value;
    h.set_enabled_cpu_counters({hwcpipe::CpuCounter::Cycles, hwcpipe::CpuCounter::Instructions});
    h.run();
    auto measurements=h.sample();
    value.append("cpu Cycles:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Cycles));
    value.append("\n");
    value.append("cpu Instructions:");
    value.append(getCpuCounterInfo(measurements, hwcpipe::CpuCounter::Instructions));
    value.append("\n");
    std::cout << value << std::endl;
    h.stop();
    int len = value.size();
    jchar res[len];
    for (int i = 0; i < len; i++) {
        res[i] = (jchar)value[i];
    }
    return env->NewString(res, len);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_at_favre_lib_hwcpipedemo_slice_MainAbilitySlice_enableGpuCounter(JNIEnv* env, jobject  obj) {
    HWCPIPE_LOG("native：enableGpuCounter");
    hwcpipe::HWCPipe h;
    h.set_enabled_gpu_counters({hwcpipe::GpuCounter::GpuCycles});
    std::string value;
    h.run();
    auto measurements=h.sample();
    value.append("gpu GpuCycles:");
    value.append(getGpuCounterInfo(measurements,hwcpipe::GpuCounter::GpuCycles));
    value.append("\n");
    std::cout << value << std::endl;
    h.stop();
    int len = value.size();
    jchar res[len];
    for (int i = 0; i < len; i++) {
        res[i] = (jchar)value[i];
    }
    return env->NewString(res, len);
}
